package nes;

import java.awt.Color;
import java.awt.Point;
import robocode.BulletHitEvent;
import robocode.HitByBulletEvent;
import robocode.AdvancedRobot;
import robocode.ScannedRobotEvent;
import robocode.WinEvent;
import robocode.util.Utils;

/**
 * Yoshimi competitive robot. Very simple framework to demostrate the buld system.
 * 
 * @author Sergey Negrashov
 * 
 */
public class Yoshimi extends AdvancedRobot {

  /**
   * Flag indicating if the opponent has been found.
   */
  private boolean opponentFound = false;

  /**
   * Flag indicating if the opponent fired last turn.
   */
  private boolean opponentFiredLastTurn = false;

  /**
   * Flag indicates that we need to fire.
   */
  boolean fireFlag = false;

  /**
   * Power of our next bullet.
   */
  double bulletPower = 0;

  private int radarSwitch = 1;

  /**
   * Abstraction for the opponent.
   */
  private Opponent opponent = null;

  /**
   * Main robocode method.
   */
  @Override
  public void run() {
    out.print("Starting battle\n");
    setAllColors(Color.PINK);
    setAdjustRadarForRobotTurn(true);
    setAdjustGunForRobotTurn(true);
    setAdjustRadarForGunTurn(true);
    while (!opponentFound) {
      turnRadarLeft(90);
    }
    out.println(getTime());
    while (true) {
      turnRadarLeft(360);
    }
  }

  /**
   * Scanned event override. Computes opponents position and stores it.
   * 
   * @param event scanned event.
   */
  @Override
  public void onScannedRobot(ScannedRobotEvent event) {

    if (!opponentFound) {
      out.println("Hello " + event.getName() + " it is a good day to die.\n");
      opponentFound = true;
      opponent = new Opponent(event.getName());
    }
    if (opponent.getName().equals(event.getName())) {
      Point me = getMe();
      double absBearing = getHeading() + event.getBearing();
      if (absBearing < 0) {
        absBearing += 360;
      }
      PolarVector vec = new PolarVector(event.getDistance(), absBearing);
      Point target = PositionHelper.projectPolarVector(me, vec);
      opponentFiredLastTurn =
          opponent.checkUpdate(event.getName(), event.getEnergy(), target, getTime());
      radarSwitch *= -1;
      if (radarSwitch > 0) {
        setTurnRight(event.getBearing() + 90);
      }
    }
    setTurnRadarLeft(radarSwitch * 360 * 100);
    if (opponentFiredLastTurn) {
      doMovement();
      doGun();
      opponentFiredLastTurn = false;
    }
    else if (checkPosition()) {
      doGun();
    }
    else {
      correctPosition();
    }
    execute();
    
    if (fireFlag) {
      fireFlag = !fireFlag;
      fire(bulletPower);
    }
  }

  /**
   * Corrects robot position, by moving away from the walls.
   */
  private void correctPosition() {
    Point me = getMe();
    Point paradise = new Point(me);
    if (me.x + 100 > getBattleFieldWidth()) {
      paradise.x -= 100;
    }
    if (me.x - 60 < 0) {
      paradise.x += 100;
    }
    if (me.y + 60 > getBattleFieldHeight()) {
      paradise.y -= 100;
    }
    if (me.y - 60 < 0) {
      paradise.y += 100;
    }
    PolarVector vec = PositionHelper.getPolarVector(me, paradise);
    setTurnRight(Utils.normalRelativeAngleDegrees(vec.getTheta() - getHeading()));
    ahead(vec.getR() + 100);
  }

  /**
   * Check if the robot is in a bad spot.
   * 
   * @return {@code true} if the position is ok, otherwise returns {@code false}.
   */
  private boolean checkPosition() {
    Point me = getMe();
    if (me.x + 60 > getBattleFieldWidth()) {
      return false;
    }
    if (me.x - 60 < 0) {
      return false;
    }
    if (me.y + 60 > getBattleFieldHeight()) {
      return false;
    }
    if (me.y - 60 < 0) {
      return false;
    }
    return true;
  }

  /**
   * Moves in randomly when opponent fired, unless the opponent is too close in which case we try to
   * fire.
   */
  private void doMovement() {
    Point lastSeen = opponent.getLastPosition();
    Point me = getMe();
    PolarVector vec = PositionHelper.getPolarVector(me, lastSeen);
    if (vec.getR() < 40) {
      doGun();
      return;
    }
    double rand = Math.random() > 0.5 ? -1 : 1;
    double scale = Math.random();
    if (scale < 0.5) {
      scale += 1;
    }
    setAhead(rand * scale * 50);
  }

  /**
   * Calculates the future coordinates, turns the gun and fires.
   */
  private void doGun() {
    Point lastSeen = opponent.getLastPosition();
    Point me = getMe();
    PolarVector vec = PositionHelper.getPolarVector(me, lastSeen);
    bulletPower = Math.max(500 / vec.getR(), 3);
    bulletPower = Math.min(bulletPower, 1);
    double bulletSpeed = 20 - bulletPower * 3;
    long time = (long) (vec.getR() / bulletSpeed);
    time += (vec.getTheta() - getGunHeading()) / 20;
    Point future = opponent.getFuturePoint(getTime() + time);
    vec = PositionHelper.getPolarVector(me, future);
    setTurnGunRight(Utils.normalRelativeAngleDegrees(vec.getTheta() - getGunHeading()));
    fireFlag = true;
  }

  /**
   * Called when our bullet hit the robot. Uses bullet coordinates to update the position.
   * 
   * @param event bullet hit event.
   */
  @Override
  public void onBulletHit(BulletHitEvent event) {
    opponent.tookDamage(event.getBullet(), getTime());
  }

  /**
   * Called when we are hit by bullet, calculates robots new energy.
   * 
   * @param event bullet hit event.
   */
  @Override
  public void onHitByBullet(HitByBulletEvent event) {
    opponent.didDamage(event.getPower(), getTime());
  }

  /**
   * Returns our robot position.
   * 
   * @return our position.
   */
  private Point getMe() {
    Point ret = new Point();
    ret.setLocation(getX(), getY());
    return ret;
  }

  @Override
  public void onWin(WinEvent e) {
    out.println("Good game sir " + opponent.getName());
    doNothing();
  }
}
